/**
* File: gui.cpp
*
* \brief Graphical User Interface for agent management.
*
* \author Instituto Superior Tecnico de Lisboa, Portugal
* \author Technical University of Lisbon, Portugal
* 
* \author Agentes Autonomos e Sistemas Multiagente
* \author Autonomous Agents and Multiagent Systems
* 
* \version	1.0
* \date		2006/2007
**/
#include "gui.h"
using namespace game::gui;
#include <assert.h>
#include "waypoint.h"
#include "../engineApi.h"
#include "../modApi.h"
#include "../util/language.h"
#include "../util/util.h"
#include "../util/tools/WaypointAnalyser.h"
using namespace util;
using namespace util::tools;
#include "../game/globals.h"
#include "../game/Tournament.h"
#include "../game/World.h"
using namespace game;
#include "../agents/Agent.h"
#include "../agents/globals.h"
#include "../agents/TerroristAgent.h"
#include "../agents/CounterTerroristAgent.h"
#include "../agents/samples/AgentAndWorldSampleAgent.h"
#include "../agents/samples/CombatAndFinancesSampleAgent.h"
#include "../agents/samples/NavigationSampleAgent.h"
#include "../agents/samples/CommunicationSampleAgent.h"
#include "../agents/samples/SensesSampleAgent.h"
#include "../agents/ReactiveTerroristAgent.h"
#include "../agents/ReactiveCounterTerroristAgent.h"
#include "../agents/BdiCommCounterTerroristAgent.h"
#include "../agents/BdiCommTerroristAgent.h"
#include "../agents/BdiCounterTerroristAgent.h"
#include "../agents/BdiTerroristAgent.h"
#include "../agents/TournamentCounterTerroristAgent.h"
#include "../agents/TournamentTerroristAgent.h"
#include "../agents/AdditionalTopicCounterTerroristAgent.h"
#include "../agents/AdditionalTopicTerroristAgent.h"

using namespace agents;
using namespace agents::samples;


//=============================================================================
// Prototypes
//=============================================================================
Agent* createTerroristAgent(edict_t* enity, int model, const char* mind);
Agent* createCounterTerroristAgent(edict_t* enity, int model, const char* mind);

//=============================================================================
// Variables
//=============================================================================

/**  \Text and key flags for menus 
\\y & \\w are special CS colour tags **/
MenuText game::gui::menuMain = {
	0x2ff,
		"\\yAgent Options\\w\n\n"
		"1. Quick add agent\n"
		"2. Add specific agent\n"
		"3. Kill all agents\n"
		"4. Kill all players\n"
		"5. Fill server with agents\n"
		"6. Kick random agent\n"
		"7. Remove all agents\n"
		"0. Exit"
};

MenuText game::gui::menuSelectTeam = {
	0x213,
		"\\ySelect a team\\w\n\n"
		"1. Terrorist\n"
		"2. Counter-Terrorist\n"
		"5. Auto-select\n"
		"0. Exit"
};

MenuText game::gui::menuSelectMind = {
	0x3ff,
		"\\ySelect a mind\\w\n\n"
		"1. Reactive\n"
		"2. BDI\n"
		"3. BDI with Communication\n"
		"4. Additional topic\n"
		"5. Tournament\n"
		"6. Navigation sample\n"
		"7. Combat & Finances sample\n"
		"8. Senses sample\n"
		"9. Communication sample\n"
		"0. Exit"
};

MenuText game::gui::menuWaypointMenu = {
	0x3ff,
		"\\yWaypoint Operations (Page 1)\\w\n\n"
		"1. Show/Hide waypoints\n"
		"2. Create path begin\n"
		"3. Create path end\n"
		"4. Remove path begin\n"
		"5. Remove path end\n"
		"6. Add waypoint\n"
		"7. Delete waypoint\n"
		"8. Set Radius\n\n"
		"9. Next...\n\n"
		"0. Exit"
};

MenuText game::gui::menuWaypointMenu2 = {
	0x3ff,
		"\\yWaypoint Operations (Page 2)\\w\n\n"
		"1. Waypoint stats\n"
		"2. Autowaypoint on/off\n"
		"3. Set flags\n"
		"4. Save waypoints\n"
		"5. Save without checking\n"
		"6. Load waypoints\n"
		"7. Check waypoints\n"
		"8. Noclip cheat on/off\n\n"
		"9. Previous...\n\n"
		"0. Exit"
};

MenuText game::gui::menuWaypointRadius = {
	0x3ff,
		"\\yWaypoint Radius\\w\n\n"
		"1. 0\n"
		"2. 8\n"
		"3. 16\n"
		"4. 32\n"
		"5. 48\n"
		"6. 64\n"
		"7. 80\n"
		"8. 96\n"
		"9. 128\n\n"
		"0. Exit"
};

MenuText game::gui::menuWaypointAdd = {
	0x3ff,
		"\\yWaypoint Type\\w\n\n"
		"1. Normal\n"
		"\\r2. Terrorist Important\n"
		"3. Counter-Terrorist Important\n"
		"\\w4. Block with hostage\n"
		"\\y5. Rescue Zone\n"
		"\\w6. Camping\n"
		"7. Camp End\n"
		"\\r8. Map Goal\n"
		"\\w9. Jump\n\n"
		"\\w0. Exit"
};

MenuText game::gui::menuWaypointFlag = {
	0x2ff,
		"\\yWaypoint Flags\\w\n\n"
		"\\yAdd waypoint flag:\\w\n\n"
		"1. Block with Hostage\n"
		"2. Terrorists Specific\n"
		"3. CTs Specific\n"
		"4. Use Elevator\n\n"
		"\\yDelete waypoint flag:\\w\n\n"
		"5. Block with Hostage\n"
		"6. Terrorists Specific\n"
		"7. CTs Specific\n"
		"8. Use Elevator\n\n"
		"0. Exit"
};

AgentCreationRecord game::gui::g_agentCreationTabs[32];
int g_agentNameCounter = 0;

/**
* \brief Whether a parameter is valid.
**/
bool parameterIsValid(const char *str) {
	if (util::isNullString(str))
		return false;
	return (*str != '*' || *(str + 1) != '\0');
}

/**
* \brief Setup creation of an agent.
*
* \param[in] team The team. Set to 'NULL' for random team.
* \param[in] name The name. Set to 'NULL' for default name.
* \param[in] mind The mind type. Set to 'NULL' for default name.
* \param[in] model The model. Set to 'NULL' for random model.
**/
void game::gui::addAgent(const char *team, const char *name, const char* mind, const char *model)
{
	if(g_tournament.isOn())
		return;

	// Search creation tab for a free slot
	int index = 0;
	while (g_agentCreationTabs[index].bNeedsCreation && index < gpGlobals->maxClients)
		index++;

	if (index >= gpGlobals->maxClients)
		return; // Maximum number of players reached. Can't create agent!

	g_agentCreationTabs[index].bNeedsCreation = true; // Needs to be created
	g_agentCreationTabs[index].pCallingEnt = g_world->hostEdict; // Entity who issued it

	// Copy arguments
	if (parameterIsValid(name))
		strncpy(g_agentCreationTabs[index].name, name, 21);
	else
		g_agentCreationTabs[index].name[0] = '\0';

	if (parameterIsValid(team))
		strncpy(g_agentCreationTabs[index].team, team, 1);
	else
		strcpy(g_agentCreationTabs[index].team, "5");

	if (parameterIsValid(model))
		strncpy(g_agentCreationTabs[index].model, model, 3);
	else
		strcpy(g_agentCreationTabs[index].model, "-1");

	if (parameterIsValid(mind))
		strncpy(g_agentCreationTabs[index].mind, mind, 64);
	else
		strcpy(g_agentCreationTabs[index].mind, "default");

	if (g_agentCreationTime == 0) // Create soon...
		g_agentCreationTime = g_world->getTime();
}

/**
* \brief Fills the server with agents.
*
* \param[in] iSelection
* \param[in] iPersonality [DEPRECATED]
**/
void game::gui::fillServer(int iSelection, int iPersonality)
{
	int index;

	if (iSelection == 1 || iSelection == 2)
	{
		CVAR_SET_STRING("mp_limitteams","0");
		CVAR_SET_STRING("mp_autoteambalance","0");
	}
	else
		iSelection = 5;

	for (index = 0; index < gpGlobals->maxClients; index++)
	{
		if (!g_agentCreationTabs[index].bNeedsCreation)
		{
			g_agentCreationTabs[index].bNeedsCreation = true;
			g_agentCreationTabs[index].pCallingEnt = NULL;
			g_agentCreationTabs[index].name[0] = '\0';
			sprintf(g_agentCreationTabs[index].team, "%d", iSelection);
		}
	}

	if (g_agentCreationTime == 0.0)
		g_agentCreationTime = g_world->getTime();
}

/**
* \brief Removes all agents.
**/
void game::gui::removeAllAgents(void)
{
	for (int index = 0; index < gpGlobals->maxClients; index++)
	{
		// Reset our Creation Tab if there are still Bots waiting
		// to be spawned
		g_agentCreationTabs[index].bNeedsCreation = false;
		g_agentCreationTabs[index].pCallingEnt = NULL;
		g_agentCreationTabs[index].name[0] = '\0';
		g_agentCreationTabs[index].team[0] = '\0';

		if (g_world->agents[index])  // is this slot used?
			SERVER_COMMAND(util::varArgs("kick \"%s\"\n", STRING(g_world->agents[index]->pev->netname)));  // kick the agent using (kick "name")
	}

	char command[32];
	getServerCommandPrefix(command);
	strcat(command, "_quota");
	CVAR_SET_FLOAT(command, 0);
	g_agentQuota = 0;
}

/**
* \brief Kills all agents.
**/
void game::gui::killAllAgents(void)
{
	edict_t *pPlayer;

	for (int ind = 1; ind <= gpGlobals->maxClients; ind++)
	{
		pPlayer = INDEXENT(ind);

		// is this player slot valid?
		if (pPlayer && !pPlayer->free)
		{
			// is this player an agent?
			if (pPlayer->v.flags & (FL_FAKECLIENT | (1<<27))
				|| Agent::getInstance(pPlayer) != NULL)
			{
				if (util::isAlive(pPlayer))
				{
					pPlayer->v.frags += 1;
					MDLL_ClientKill(pPlayer);
				}
			}
		}
	}
}

/**
* \brief Kicks a random agent.
**/
void game::gui::kickRandomAgent(void)
{
	for (int index = 0; index < gpGlobals->maxClients; index++)
	{
		if (g_world->agents[index])  // is this slot used?
		{
			// kick the agent using (kick "name")
			SERVER_COMMAND(util::varArgs("kick \"%s\"\n", STRING(g_world->agents[index]->pev->netname)));
			break;
		}
	}
}

/**
* 
**/
void game::gui::newroundAll(void)
{
	edict_t *pPlayer;

	for (int ind = 1; ind <= gpGlobals->maxClients; ind++)
	{
		pPlayer = INDEXENT(ind);
		// is this player slot valid
		if (pPlayer && !pPlayer->free)
		{
			if (pPlayer->v.flags & (FL_CLIENT | FL_FAKECLIENT))
			{
				if (util::isAlive(pPlayer))
				{
					pPlayer->v.frags += 1;
					MDLL_ClientKill(pPlayer);
				}
			}
		}
	}
}


/**
* \brief Assigns a name to the agent. 
*
* If no name is passed as an argument, a name is provided for the agent.
*
* \param[in] nameArg The argument passed in representing the desired name.
* \param[out] nameBuffer The final agent's name. 
**/
void createAgentName(const char* nameArg, char* nameBuffer)
{	
	char c_name[AGENT_NAME_MAX_LENGTH + 1];
	if (util::isNullString(nameArg)) // If no name is given, do our name stuff
		sprintf(c_name, "Agent%d", g_agentNameCounter++);
	else strncpy(c_name, nameArg, 21);

	char modName[16];
	util::getModName(modName);
	sprintf(nameBuffer,"[%s] %s", modName, c_name);
}

/**
* \brief Setup creation of an agent.
*
* \param[in] pPlayer The Half-Life engine associated entity.
* \param[in] teamArg The team.
* \param[in] nameArg The name.
* \param[in] mindArg The mind type.
* \param[in] classArg The model.
**/
void game::gui::createAgent(edict_t *pPlayer, 
			   const char *teamArg, const char *nameArg, const char *mindArg, const char *classArg)
{
	edict_t *enity;

	// Don't allow creating Bots when no waypoints are loaded
	if (g_numWaypoints < 1)
	{
		if (!FNullEnt(pPlayer))
			util::hostPrint(pPlayer, print_center, "No Waypoints for this Map!\n");

		SERVER_PRINT(util::language::translate("No Waypoints for this Map!\n"));

		g_agentCreationTime = 0.0;
		char command[32];
		getServerCommandPrefix(command);
		strcat(command, "_quota");
		CVAR_SET_FLOAT(command, 0);
		g_agentQuota = 0;
		return;
	}
	// If Waypoints have changed don't allow it because Distance Tables are messed up
	else if (g_waypointsChanged)
	{
		if (pPlayer)
			util::hostPrint(pPlayer, print_center, "Waypoints changed or not initialized!\n");

		SERVER_PRINT("Waypoints changed or not initialized!\n");

		g_agentCreationTime = 0.0;
		return;
	}


	//Assign a name to the agent
	char c_fullname[AGENT_NAME_MAX_LENGTH + 1];
	createAgentName(nameArg, c_fullname);

	//Invoke the engine's API call to create the fake client
	enity = g_engfuncs.pfnCreateFakeClient(c_fullname);

	//Maximum number of players reached?
	if (FNullEnt( enity ))
	{
		g_agentCreationTime = 0.0;
		return;
	}

	//Index is valid?
	int index = ENTINDEX(enity) - 1;
	assert(index >= 0);
	assert(index < 32);

	//Create the object representing the agent on the client's side
	assert(g_world->agents[index] == NULL);
	int team = util::isNullString(teamArg) ? 5 : atoi(teamArg);
	int model = util::isNullString(classArg) ? RANDOM_LONG(1, 4) : atoi(classArg);
	char mind[64];
	if(util::isNullString(mindArg))
		strcpy(mind,"default");
	else
		strcpy(mind, mindArg);
	if(team == CounterStrikeTeams::terrorist)
		g_world->agents[index] = createTerroristAgent(enity, model, mindArg);
	else 
		g_world->agents[index] = createCounterTerroristAgent(enity, model, mindArg);

	if (g_world->agents[index] == NULL)
		util::terminateOnError("Memory Allocation Error!");
}
/**
* \brief Creates the terrorist agent according to the mind type.
*
* \param[in] enity The Half-Life engine associated entity.
* \param[in] model The model.
* \param[in] mind The mind type.
**/
Agent* createTerroristAgent(edict_t* enity, int model, const char* mind)
{
	if(strcmp(mind, "combatAndFinancesSample") == 0)
		return new CombatAndFinancesSampleAgent(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "agentAndWorldSample") == 0)
		return new AgentAndWorldSampleAgent(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "navigationSample") == 0)
		return new NavigationSampleAgent(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "communicationSample") == 0)
		return new CommunicationSampleAgent(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "sensesSample") == 0)
		return new SensesSampleAgent(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "waypointAnalyser") == 0)
		return new WaypointAnalyser(enity, CounterStrikeTeams::terrorist, model);
	else if(strcmp(mind, "reactive") == 0)
		return new ReactiveTerroristAgent(enity, model);
	else if(strcmp(mind, "bdi") == 0)
		return new BdiTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "bdiComm") == 0)
		return new BdiCommTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "additionalTopic") == 0)
		return new AdditionalTopicTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "tournament") == 0)
		return new TournamentTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else //if(strcmp(mind, "default") == 0)
		return new ReactiveTerroristAgent(enity, model);		//TOFIX: Change accordingly
}
/**
* \brief Creates the counter-terrorist agent according to the mind type.
*
* \param[in] enity The Half-Life engine associated entity.
* \param[in] model The model.
* \param[in] mind The mind type.
**/
Agent* createCounterTerroristAgent(edict_t* enity, int model, const char* mind)
{
	if(strcmp(mind, "combatAndFinancesSample") == 0)
		return new CombatAndFinancesSampleAgent(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "agentAndWorldSample") == 0)
		return new AgentAndWorldSampleAgent(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "navigationSample") == 0)
		return new NavigationSampleAgent(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "communicationSample") == 0)
		return new CommunicationSampleAgent(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "sensesSample") == 0)
		return new SensesSampleAgent(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "waypointAnalyser") == 0)
		return new WaypointAnalyser(enity, CounterStrikeTeams::counterTerrorist, model);
	else if(strcmp(mind, "reactive") == 0)
		return new ReactiveCounterTerroristAgent(enity, model);
	else if(strcmp(mind, "bdi") == 0)
		return new BdiCounterTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "bdiComm") == 0)
		return new BdiCommCounterTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "additionalTopic") == 0)
		return new AdditionalTopicCounterTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else if(strcmp(mind, "tournament") == 0)
		return new TournamentCounterTerroristAgent(enity, model);		//TOFIX: Change accordingly
	else //if(strcmp(mind, "default") == 0)
		return new ReactiveCounterTerroristAgent(enity, model);		//TOFIX: Change accordingly
}
/**
* \brief This message is sent when a VGUI menu is displayed.
**/
void game::gui::client_CS_VGUI(void *p, int agentIndex)
{
	if (g_msgState == 0)
	{
		switch (*(int *)p)
		{
		case MENU_TEAM:
			g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
			break;
		case MENU_TERRORIST:
		case MENU_CT:
			g_world->agents[agentIndex]->startAction = GameStartActions::classSelect;
			break;
		}
	}
	g_msgState++;
}

/**
* \brief This message is sent when a text menu is displayed.
**/
void game::gui::client_CS_ShowMenu(void *p, int agentIndex)
{
	if (g_msgState < 3)
	{
		g_msgState++;  // ignore first 3 fields of message
		return;
	}

	if (strcmp((char *)p, "#Team_Select") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#Team_Select_Spect") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#IG_Team_Select_Spect") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#IG_Team_Select") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#IG_VIP_Team_Select") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#IG_VIP_Team_Select_Spect") == 0)  // team select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::teamSelect;
	else if (strcmp((char *)p, "#Terrorist_Select") == 0)  // T model select?
		g_world->agents[agentIndex]->startAction = GameStartActions::classSelect;
	else if (strcmp((char *)p, "#CT_Select") == 0)  // CT model select menu?
		g_world->agents[agentIndex]->startAction = GameStartActions::classSelect;
}


/**
* \brief This message is sent when a client joins the game.  
*
* All of the weapons are sent with the weapon ID and
* information about what ammo is used.
**/
void game::gui::client_CS_WeaponList(void *p, int agentIndex)
{
	static Weapon weapon;

	switch (g_msgState)
	{
	case 0:
		strcpy(weapon.classname, (char *)p);
		break;

	case 1:
		weapon.ammo = *(int *)p;  // ammo index 1
		break;

	case 2:
		weapon.ammoMax = *(int *)p;  // max ammo1
		break;

	case 5:
		weapon.slot = *(int *)p;  // slot for this weapon
		break;

	case 6:
		weapon.slotPosition = *(int *)p;  // position in slot
		break;

	case 7:
		weapon.id = *(int *)p;  // weapon ID
		break;

	case 8:
		weapon.flags = *(int *)p;  // flags for weapon
		g_weaponDefinitions[weapon.id] = weapon;  // store away this weapon with it's ammo information...
		break;
	}
	g_msgState++;
}



/**
* \brief This message is sent when a weapon is selected (either
* by the agent chosing a weapon or by the server auto
* assigning the agent a weapon). 
*
* In CS it's also called when ammo is increased/decreased.
**/
void game::gui::client_CS_CurrentWeapon(void *p, int agentIndex)
{
	static int iState;
	static int id;
	static int iClip;

	Agent *agent = g_world->agents[agentIndex];
	switch (g_msgState)
	{
	case 0:
		iState = *(int *)p;  // state of the current weapon (WTF???)
		break;

	case 1:
		id = *(int *)p;  // weapon ID of current weapon
		break;

	case 2:
		iClip = *(int *)p;  // ammo currently in the clip for this weapon
		if (id <= 31)
		{
			if (iState != 0)
				((Agent*)agent)->combat->setCurrentWeapon((WeaponIds::Enumeration) id);

			((Agent*)agent)->combat->ammoInClip[id] = iClip;
		}
		break;
	}
	g_msgState++;
}


/**
* \brief This message is sent whenever ammo amounts are adjusted
* (up or down).
* 
* NOTE: Logging reveals that CS uses it very unreliable!
**/
void game::gui::client_CS_AmmoX(void *p, int agentIndex)
{
	static int index;
	static int amount;

	Agent *agent = g_world->agents[agentIndex];

	switch (g_msgState)
	{
	case 0:
		index = *(int *)p;  // ammo index (for type of ammo)
		break;

	case 1:
		amount = *(int *)p;  // the amount of ammo currently available

		((Agent*)agent)->combat->ammo[index] = amount;  // store it away

		break;
	}
	g_msgState++;
}



/**
* \brief This message is sent when the agent picks up some ammo
* (AmmoX messages are also sent so this message is probably
* not really necessary except it allows the HUD to draw
* pictures of ammo that have been picked up. 
* 
* The agents don't really need pictures since they don't have any
* eyes anyway.
**/
void game::gui::client_CS_AmmoPickup(void *p, int agentIndex)
{
	static int index;
	static int amount;

	switch (g_msgState)
	{
	case 0:
		index = *(int *)p;
		break;

	case 1:
		amount = *(int *)p;

		((Agent*)g_world->agents[agentIndex])->combat->ammo[index] = amount;

		break;
	}

	g_msgState++;
}


/**
* \brief This message gets sent when the agents are getting damaged.
**/
void game::gui::client_CS_Damage(void *p, int agentIndex)
{
	static int damage_armor;
	static int damage_taken;
	static int damage_bits;
	static Vector damage_origin;

	if (g_msgState == 0)
		damage_armor = *(int *)p;

	else if (g_msgState == 1)
		damage_taken = *(int *)p;

	else if (g_msgState == 2)
		damage_bits = *(int *)p;

	else if (g_msgState == 3)
		damage_origin.x = *(float *)p;

	else if (g_msgState == 4)
		damage_origin.y = *(float *)p;

	else if (g_msgState == 5)
	{
		damage_origin.z = *(float *)p;

		if (damage_armor > 0 || damage_taken > 0)
		{
			Agent *agent = g_world->agents[agentIndex];
			agent->lastDamageType = damage_bits;

			int team = util::getTeam(agent->getEdict());

			edict_t *pEnt = agent->pev->dmg_inflictor;
			if (util::isPlayer(pEnt)) //Hurt by a player
			{
				//...
			}
			else // Hurt by unusual damage like drowning or gas
			{
				//...
			}
		}
	}
	g_msgState++;
}


/**
* \brief This message gets sent when the agents money amount changes.
**/
void game::gui::client_CS_Money(void *p, int agentIndex)
{
	if (g_msgState == 0)
		((Agent*) g_world->agents[agentIndex])->finances->setMoney(*(int *)p);  // amount of money

	g_msgState++;
}


/**
* \brief This message gets sent when the HUD status icon changes.
**/
void game::gui::client_CS_StatusIcon(void *p, int agentIndex)
{
	static unsigned char buyEnable;

	if (g_msgState == 0)
		buyEnable = *(unsigned char *)p;

	else if (g_msgState == 1)
	{
		Agent *agent = (Agent*) g_world->agents[agentIndex];
		if (strcmp ((char *)p, "defuser") == 0)
			agent->combat->hasDefuser = (buyEnable != 0);
		else if (strcmp ((char *)p, "buyzone") == 0)
		{
			agent->finances->setInBuyZone(buyEnable != 0);
		}
	}

	g_msgState++;
}


/**
* \brief This message gets sent when someone got killed.
**/
void game::gui::client_CS_DeathMsg(void *p, int agentIndex)
{
	static int killer_index;
	static int victim_index;

	if (g_msgState == 0)
		killer_index = *(int *)p;  // ENTINDEX() of killer
	else if (g_msgState == 1)
		victim_index = *(int *)p;  // ENTINDEX() of victim
	else if (g_msgState == 2)
	{
		if (killer_index != 0 && killer_index != victim_index)
		{
			edict_t *killer_edict = INDEXENT(killer_index);
			edict_t *victim_edict = INDEXENT(victim_index);

			g_tournament.handleEnemyKill(killer_edict, victim_edict);
		}
	}
	g_msgState++;
}


/**
* \brief This message gets sent when the screen fades (flashbang).
**/
void game::gui::client_CS_ScreenFade(void *p, int agentIndex)
{
	static unsigned char r;
	static unsigned char g;
	static unsigned char b;

	if (g_msgState == 3)
		r = *(unsigned char *)p;
	else if (g_msgState == 4)
		g = *(unsigned char *)p;
	else if (g_msgState == 5)
		b = *(unsigned char *)p;
	else if (g_msgState == 6)
	{
		unsigned char alpha = *(unsigned char *)p;

		if (r == 255 && g == 255 && b == 255 && alpha > 200)
		{
			Agent *agent = g_world->agents[agentIndex];
			//...
		}
	}
	g_msgState++;
}

/**
* 
**/
void game::gui::client_CS_HLTV(void *p, int agentIndex)
{
	if (g_msgState == 0)
	{
		if ((*(int *)p) == 0)
			g_world->newRound();
	}
	g_msgState++;
}

/**
* 
**/
void game::gui::client_CS_TextMsgAll(void *p, int agentIndex)
{
	if (g_msgState == 1)
	{
		char *sz = (char *)p;
		if ( FStrEq(sz, "#CTs_Win")
			|| FStrEq(sz, "#Bomb_Defused"))
		{
			if(g_tournament.isOn())
				g_tournament.handleObjectiveAchieved(CounterStrikeTeams::counterTerrorist);
		}
		if ( FStrEq(sz, "#Terrorists_Win")
			|| FStrEq(sz, "#Target_Bombed"))
		{
			if(g_tournament.isOn())
				g_tournament.handleObjectiveAchieved(CounterStrikeTeams::terrorist);
		}
		if ( FStrEq(sz, "#CTs_Win")
			|| FStrEq(sz, "#Bomb_Defused")
			|| FStrEq(sz, "#Terrorists_Win")
			|| FStrEq(sz, "#Target_Bombed")
			|| FStrEq(sz, "#Target_Saved"))
		{
			if(g_tournament.isOn())
				g_tournament.readyForNewRound = true;
		}

		if ( FStrEq(sz, "#CTs_Win")
			|| FStrEq(sz, "#Bomb_Defused")
			|| FStrEq(sz, "#Terrorists_Win")
			|| FStrEq(sz, "#Round_Draw")
			|| FStrEq(sz, "#All_Hostages_Rescued")
			|| FStrEq(sz, "#Target_Saved")
			|| FStrEq(sz, "#Hostages_Not_Rescued")
			|| FStrEq(sz, "#Terrorists_Not_Escaped")
			|| FStrEq(sz, "#VIP_Not_Escaped")
			|| FStrEq(sz, "#Escaping_Terrorists_Neutralized")
			|| FStrEq(sz, "#VIP_Assassinated")
			|| FStrEq(sz, "#VIP_Escaped")
			|| FStrEq(sz, "#Terrorists_Escaped")
			|| FStrEq(sz, "#CTs_PreventEscape")
			|| FStrEq(sz, "#Target_Bombed")
			|| FStrEq(sz, "#Game_Commencing")
			|| FStrEq(sz, "#Game_will_restart_in"))
		{
			g_world->setIsRoundOver(true);
			g_world->setIsBombPlanted(false);
		}
	}
	g_msgState++;
}

/**
* 
**/
void game::gui::client_CS_TextMsg(void *p, int agentIndex)
{
	if (g_msgState == 1)
	{
		if (FStrEq((char *)p, "#Got_bomb") || FStrEq((char *)p, "#Game_bomb_pickup"))
		{
			//...
		}
	}
	g_msgState++;
}


/**
* \brief This message gets sent when a say chat message is sent.
**/
void game::gui::client_CS_SayText(void *p, int agentIndex)
{ 
	// this message gets sent when a chat message is sent
	static ChatType::Enumeration chatType;

	char message[MAX_MESSAGE_SIZE + 10];
	strcpy (message, (char *) p); // get the message

	// does it end with a carriage return ?
	if (message[strlen (message) - 1] == '\n')
		message[strlen (message) - 1] = 0; // strip the carriage return
	
	//See what say type of msg it is
	if(g_msgState == 1)
	{
		if(strcmp(message,"#Cstrike_Chat_All") == 0)
			chatType = ChatType::all;
		else if(strcmp(message,"#Cstrike_Chat_T") == 0)
			chatType = ChatType::terrorist;
		else if(strcmp(message,"#Cstrike_Chat_CT") == 0)
			chatType = ChatType::counterTerrorist;
		else if(strcmp(message,"#Cstrike_Chat_AllDead") ==0 )
			chatType = ChatType::dead;
		else 
			chatType = ChatType::unknown;
	}

	//BUG 03: Agents can't chat with each other: Start
	// Actual Message
	if (g_msgState == 3)
	{
		string str(message);
		int index = str.find_first_of(":", 0);
		string prefix = str.substr(0, index);
		int entityIndex = atoi(prefix.c_str());
		edict_t* sender = INDEXENT(entityIndex);

		Agent* agent;
		agent = (Agent*)g_world->agents[agentIndex];

		if (agent)
		{
			edict_t *receiver = agent->getEdict();
			if (util::isPlayer(receiver) && (receiver != sender))
			{
				int length = str.length();
				string substr = str.substr(index + 1, length - index - 1);
				const char* content = substr.c_str();
				if(util::isAlive(sender))
				{
					switch(chatType)
					{
					case ChatType::all:
						if(g_tournament.isOn() && g_tournament.isTournamentMaster())
							if(strcmp(message, "_TOURNAMENT_EXCEPTION_") == 0)
							{
								g_tournament.handleException(false);
								break;
							}

						agent->communication->pushSayMessage(content, sender);
						break;
					case ChatType::counterTerrorist:
						if(agent->getTeam() == CounterStrikeTeams::counterTerrorist)
							agent->communication->pushSayTeamMessage(content, sender);
						break;
					case ChatType::terrorist:
						if(agent->getTeam() == CounterStrikeTeams::terrorist)
							agent->communication->pushSayTeamMessage(content, sender);
						break;
					}
				}
				//Sender is dead. Only dead agents should hear this.
				else if(chatType == ChatType::dead)
				{
					agent->communication->pushSayDeadMessage(message, sender);
				}
			}
		}		
	}
	//BUG 03: End
	g_msgState++;
}
